use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;


use crate::sm3::error::Error;


pub struct PaddedFileBuffer{
    reader:Option<BufReader<File>>,
    frame_buffer:[u8;64],
    extend_frame_buffer:[u8;64],
    content_length:usize,
    read_length:usize,
    has_extra_padding_frame:bool,
    has_read_all:bool
}




impl PaddedFileBuffer{

    #[allow(dead_code)]
    fn test_new()->Result<Self,Error>{
        Ok(Self{
            reader:None,
            frame_buffer:[0u8;64],
            extend_frame_buffer:[0u8;64],
            read_length:0,
            content_length:0,
            has_extra_padding_frame:false,
            has_read_all:false,
        })
    }


    pub fn new(file_path:&str)->Result<Self,Error>{
        if let Ok(fs) = File::open(file_path) {
            Ok(Self {
                reader:Some(BufReader::new(fs)),
                frame_buffer:[0;64],
                extend_frame_buffer:[0;64],
                read_length:0,
                content_length:0,
                has_extra_padding_frame:false,
                has_read_all:false,
            })
        } else {
            let mut msg = String::from("Failed to open file:");
            msg.push_str(file_path);
            Err(Error::new(msg))
        }
    }

    pub fn iter_one_frame_copy(&mut self)->Option<[u8;64]>{
        if self.reader.is_none() {
            return None;
        }

        if self.has_read_all && self.has_extra_padding_frame {
            #[cfg(feature="print_padded_file_buffer")]
            println!("[PaddedFileBuffer]:return extend_frame_buffer");
            #[cfg(feature="print_padded_file_buffer")]
            println!("[PaddedFileBuffer]:iter_one_frame_copy:{:x?}",self.extend_frame_buffer);

            self.has_extra_padding_frame = false;
            return Some(self.extend_frame_buffer);
        }

        //self.frame_buffer = [0;64];
        //self.extend_frame_buffer = [0;64];

        let reader = self.reader.as_mut().unwrap();

        match reader.read(&mut self.frame_buffer){
           Ok(n) => {
               if n == 0 && self.has_read_all {
                   return None;
               }
               
               self.content_length += n;
               self.read_length = n;
               if n == 64 {
                   #[cfg(feature="print_padded_file_buffer")]
                   println!("[PaddedFileBuffer]:Read length ({}) is full for frame",self.read_length);
                   #[cfg(feature="print_padded_file_buffer")]
                   println!("[PaddedFileBuffer]:Iter_one_frame_copy:{:x?}",self.frame_buffer);

                   self.has_read_all = false;
                   return Some(self.frame_buffer);
               } else {
                   self.has_read_all = true;

                   #[cfg(feature="print_padded_file_buffer")]
                   println!("[PaddedFileBuffer]:Read length ({}) is not enough for frame, need padding...",self.read_length);

                   let k = self.calculate_pad_length_in_bits(self.read_length*8);
                   self.padding_segment(k).unwrap();
                   self.padding_tail(self.content_length).unwrap();
 
                   #[cfg(feature="print_padded_file_buffer")]
                   println!("[PaddedFileBuffer]:iter_one_frame_copy:{:x?}",self.frame_buffer);
                   return Some(self.frame_buffer);
              }
           }
          _=> None
        }
   }






 
   fn calculate_pad_length_in_bits(&self,bit_len:usize)->usize{
       let m = bit_len % 512;
       let length = match m {
           447.. => 512+448-(m+1),
           _ => 448 - (m+1),
       };
       #[cfg(feature="print_padded_file_buffer")]
       println!("[PaddedFileBuffer]:Calculated padding length in bits:{}",length);
       length
   }




   fn padding_segment(&mut self,k_in_bits:usize)->Result<(),Error>{
       self.frame_buffer[self.read_length] = 128u8;//一般情况下，数据都是整BYTE，因此消息尾必定是128，k必定大于3？
       for _i in 0..((k_in_bits-7)/8) {
           if _i+self.read_length+1 < self.frame_buffer.len(){
                self.frame_buffer[_i+self.read_length+1]=0u8;
           } else {
               self.extend_frame_buffer[_i+self.read_length+1-self.frame_buffer.len()] = 0u8;
               self.has_extra_padding_frame = true;
           }
       }
       if self.has_extra_padding_frame {
           #[cfg(feature="print_padded_file_buffer")]
           println!("[PaddedFileBuffer]:Padding need extra frame");
           #[cfg(feature="print_padded_file_buffer")]
           println!("[PaddedFileBuffer]:Buffer after padding segment:{:x?}",self.frame_buffer);
           #[cfg(feature="print_padded_file_buffer")]
           println!("[PaddedFileBuffer]:Extend buffer after padding segment:{:x?}",self.extend_frame_buffer);
       } else {
           #[cfg(feature="print_padded_file_buffer")]
           println!("[PaddedFileBuffer]:Buffer after padding segment:{:x?}",self.frame_buffer);
       }
       Ok(())
   }
 
   
   fn padding_tail(&mut self,buffer_len:usize)-> Result<(),Error> {
       let bit_len = buffer_len * 8;
       for _i in 0..8{
           if self.has_extra_padding_frame {
                self.extend_frame_buffer[64-8+_i] = (bit_len >> 8*(7-_i)) as u8;
           } else {
                self.frame_buffer[64-8+_i] = (bit_len >> 8*(7-_i)) as u8;
           }

       }

       if self.has_extra_padding_frame {
            #[cfg(feature="print_padded_file_buffer")]
            println!("[PaddedFileBuffer]:Buffer after padding tail:{:x?}",self.frame_buffer);
            #[cfg(feature="print_padded_file_buffer")]
            println!("[PaddedFileBuffer]:Extend buffer after padding tail:{:x?}",self.extend_frame_buffer);
       } else {
           #[cfg(feature="print_padded_file_buffer")]
           println!("[PaddedFileBuffer]:Buffer after padding tail:{:x?}",self.frame_buffer);
 
       }
      Ok(())
   }
  


}









#[cfg(test)]
mod tests{
    use super::*;

    #[test]
    fn test_calculate_pad_length_in_bits() {
        let padder = PaddedFileBuffer::test_new().unwrap();
        let test = 800;
        let expect = 159;
        assert_eq!(expect,padder.calculate_pad_length_in_bits(test));

        let test = 120;
        let expect = 327;
        assert_eq!(expect,padder.calculate_pad_length_in_bits(test));

        let test = 1000;
        let expect = 471;
        assert_eq!(expect,padder.calculate_pad_length_in_bits(test));

    }

    #[test]
    fn test_padding_segment() {
        let mut padder = PaddedFileBuffer::test_new().unwrap();
        let rtn = padder.padding_segment(31);
        assert_eq!(true,rtn.is_ok());
        assert_eq!(vec![
                    0x80u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,
                    0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,
                    0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,
                    0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,
                    0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,
                    0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,
                    0x00u8,0x00u8,0x00u8,0x00u8
                    ],padder.frame_buffer);
    }


    #[test]
    fn test_padding_tail() {
        let mut padder = PaddedFileBuffer::test_new().unwrap();
        let rtn = padder.padding_tail(20);
        assert_eq!(true,rtn.is_ok());
        assert_eq!(vec![
                    0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,
                    0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,
                    0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,
                    0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,
                    0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,
                    0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,0x00u8,
                    0x00u8,0x00u8,0x00u8,160u8
                   ],padder.frame_buffer);
    }

    #[test]
    fn test_binary_padding(){
        let mut file_test = File::create("./test_file_buffer").unwrap();
        let content = [0x61u8,0x62u8,0x63u8];
        file_test.write(&content).unwrap();

        //61626380 00000000 00000000 00000000 00000000 00000000 00000000 00000000
        //00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000018
        let mut padder = PaddedFileBuffer::new("./test_file_buffer").unwrap();
        let frame = padder.iter_one_frame_copy();
        std::fs::remove_file("./test_file_buffer").unwrap();
        assert_eq!(true,frame.is_some());
        assert_eq!(&[
                        0x61u8,0x62u8,0x63u8,0x80u8,
                        0x00u8,0x00u8,0x00u8,0x00u8,
                        0x00u8,0x00u8,0x00u8,0x00u8,
                        0x00u8,0x00u8,0x00u8,0x00u8,
                        0x00u8,0x00u8,0x00u8,0x00u8,
                        0x00u8,0x00u8,0x00u8,0x00u8,
                        0x00u8,0x00u8,0x00u8,0x00u8,
                        0x00u8,0x00u8,0x00u8,0x00u8,
                        0x00u8,0x00u8,0x00u8,0x00u8,
                        0x00u8,0x00u8,0x00u8,0x00u8,
                        0x00u8,0x00u8,0x00u8,0x00u8,
                        0x00u8,0x00u8,0x00u8,0x00u8,
                        0x00u8,0x00u8,0x00u8,0x00u8,
                        0x00u8,0x00u8,0x00u8,0x00u8,
                        0x00u8,0x00u8,0x00u8,0x00u8,
                        0x00u8,0x00u8,0x00u8,0x18u8
                        ],&frame.unwrap());
    }




}

